from mg_app_framework.components import TaskKey
from mg_app_framework.config import Store, get_handler, get_logger, get_organization, set_handler
from mg_app_framework.process import start_next_task
from redis import asyncio as aioredis
from tornado import gen


class RedisClient:
    def __init__(
        self,
        host,
        port,
        username,
        password,
        organization_name,
        reconnect_interval,
        redis_conn_pool_config,
    ):

        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.organization_name = organization_name
        self.connection = None
        self.reconnect_interval = reconnect_interval
        self.redis_conn_pool_config = redis_conn_pool_config

    async def get_connection(self):

        if not self.connection:
            try:
                self.connection = await aioredis.Redis(
                    host=self.host,
                    port=self.port,
                    username=self.username,
                    password=self.password,
                    decode_responses=True,  # 自动解码响应
                    **self.redis_conn_pool_config,
                )
                get_logger().info("connect redis success")
            except Exception:
                self.connection = None
                get_logger().error(
                    "connect redis failed, [host=%s],[port=%s],[username=%s],[password=%s]",
                    self.host,
                    self.port,
                    self.username,
                    self.password,
                )
        return self.connection


class RedisConnectError(Exception):
    def __init__(self):
        Store.get_loop().stop()


async def redis_async_connect(re_conn=False, conn_time=None):
    __cnt = 0
    while True:
        try:
            if conn_time and __cnt == int(conn_time):
                get_logger().error(
                    "redis async connect error,connect more than " + str(conn_time) + " times"
                )
                raise RedisConnectError
            store = Store.get_init_task_config(TaskKey.redis_async)
            organization_name = get_organization()
            host = store.get_redis_host()
            port = int(store.get_redis_port())
            username = store.get_redis_username()
            password = store.get_redis_password()
            reconnect_interval = store.reconnect_interval()
            redis_conn_pool_config = store.get_redis_conn_pool_config()
            redis_handler = RedisClient(
                host,
                port,
                username,
                password,
                organization_name,
                reconnect_interval,
                redis_conn_pool_config,
            )
            connector = await redis_handler.get_connection()
            set_handler(TaskKey.redis_async, connector)
            if not re_conn:
                start_next_task(TaskKey.redis_async)
                break
            else:
                return connector
        except Exception:
            __cnt += 1
            get_logger().exception("redis async connecting retry number: " + str(__cnt))
            store = Store.get_init_task_config(TaskKey.redis_async)
            await gen.sleep(store.reconnect_interval())


def build_mg_redis_key(key: str, app_module_name: str = None):
    """
    构建redis的key
    :param key:
    :param app_module_name:
    :return:
    """
    if not app_module_name:
        app_module_name = Store.get_app_module_name()
    return f"{app_module_name}~{key}"


def get_redis_conn() -> aioredis.Redis:
    return get_handler(TaskKey.redis_async)
